﻿using Windows.UI.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;

namespace PanViewLibrary
{
    [ContentProperty(Name = "Content")]
    public class PanView : ContentControl
    {
        public PanView()
        {
            DefaultStyleKey = typeof(PanView);
            ManipulationMode = ManipulationModes.All;

            CurrentTransformation = new CompositeTransform();
            PreviousTransformations = new MatrixTransform() { Matrix = Matrix.Identity };
            TransformGroup = new TransformGroup();
            TransformGroup.Children.Add(PreviousTransformations);
            TransformGroup.Children.Add(CurrentTransformation);

            ManipulationStarting += (sender, args) => { args.Handled = true; };
            ManipulationStarted += OnManipulationStarted;
            ManipulationDelta += OnManipulationDelta;
            ManipulationCompleted += OnManipulationCompleted;
            ManipulationInertiaStarting += (sender, args) => { args.Handled = true; };

            MinTranslateX = double.MinValue;
            MaxTranslateX = double.MaxValue;
            MinTranslateY = double.MinValue;
            MaxTranslateY = double.MaxValue;
        }

        public void Reset()
        {
            CurrentTransformation.Reset();
            PreviousTransformations.Matrix = Matrix.Identity;
        }

        public void Reset(CompositeTransform compositeTransform)
        {
            CurrentTransformation.Copy(compositeTransform);
            PreviousTransformations.Matrix = Matrix.Identity;
        }

        void OnManipulationStarted(object sender, ManipulationStartedRoutedEventArgs args)
        {
            CurrentTransformation.CenterX = args.Position.X;
            CurrentTransformation.CenterY = args.Position.Y;
            args.Handled = true;
        }

        private void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs args)
        {
            var delta = ConstrainDelta(args.Delta);
            CurrentTransformation.TranslateX += delta.Translation.X;
            CurrentTransformation.TranslateY += delta.Translation.Y;
            CurrentTransformation.Rotation += delta.Rotation;
            CurrentTransformation.ScaleX *= delta.Scale;
            CurrentTransformation.ScaleY *= delta.Scale;
            args.Handled = true;
        }

        private void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            PreviousTransformations.Matrix = TransformGroup.Value;
            CurrentTransformation.Reset();
        }

        private ManipulationDelta ConstrainDelta(ManipulationDelta delta)
        {
            var newTranslateX = PreviousTransformations.Matrix.OffsetX + CurrentTransformation.TranslateX + delta.Translation.X;

            var tooLittleX = MinTranslateX - newTranslateX;
            if (tooLittleX > 0)
            {
                delta.Translation.X += tooLittleX;
            }

            var tooMuchX = newTranslateX - MaxTranslateX;
            if (tooMuchX > 0)
            {
                delta.Translation.X -= tooMuchX;
            }

            var newTranslateY = PreviousTransformations.Matrix.OffsetY + CurrentTransformation.TranslateY + delta.Translation.Y;

            var tooLittleY = MinTranslateY - newTranslateY;
            if (tooLittleY > 0)
            {
                delta.Translation.Y += tooLittleY;
            }

            var tooMuchY = newTranslateY - MaxTranslateY;
            if (tooMuchY > 0)
            {
                delta.Translation.Y -= tooMuchY;
            }

            return delta;
        }

        public CompositeTransform CurrentTransformation
        {
            get { return (CompositeTransform)GetValue(CurrentTransformationProperty); }
            private set { SetValue(CurrentTransformationProperty, value); }
        }

        public static readonly DependencyProperty CurrentTransformationProperty =
            DependencyProperty.Register("CurrentTransformation", typeof(CompositeTransform), typeof(PanView), new PropertyMetadata(null));

        public MatrixTransform PreviousTransformations
        {
            get { return (MatrixTransform)GetValue(PreviousTransformationsProperty); }
            private set { SetValue(PreviousTransformationsProperty, value); }
        }

        public static readonly DependencyProperty PreviousTransformationsProperty =
            DependencyProperty.Register("PreviousTransformations", typeof(MatrixTransform), typeof(PanView), new PropertyMetadata(null));

        public TransformGroup TransformGroup
        {
            get { return (TransformGroup)GetValue(TransformGroupProperty); }
            private set { SetValue(TransformGroupProperty, value); }
        }

        public static readonly DependencyProperty TransformGroupProperty =
            DependencyProperty.Register("TransformGroup", typeof(TransformGroup), typeof(PanView), new PropertyMetadata(null));

        public double MinTranslateX
        {
            get { return (double)GetValue(MinTranslateXProperty); }
            set { SetValue(MinTranslateXProperty, value); }
        }

        public static readonly DependencyProperty MinTranslateXProperty =
            DependencyProperty.Register("MinTranslateX", typeof(double), typeof(PanView), new PropertyMetadata(null));

        public double MaxTranslateX
        {
            get { return (double)GetValue(MaxTranslateXProperty); }
            set { SetValue(MaxTranslateXProperty, value); }
        }

        public static readonly DependencyProperty MaxTranslateXProperty =
            DependencyProperty.Register("MaxTranslateX", typeof(double), typeof(PanView), new PropertyMetadata(null));

        public double MinTranslateY
        {
            get { return (double)GetValue(MinTranslateYProperty); }
            set { SetValue(MinTranslateYProperty, value); }
        }

        public static readonly DependencyProperty MinTranslateYProperty =
            DependencyProperty.Register("MinTranslateY", typeof(double), typeof(PanView), new PropertyMetadata(null));

        public double MaxTranslateY
        {
            get { return (double)GetValue(MaxTranslateYProperty); }
            set { SetValue(MaxTranslateYProperty, value); }
        }

        public static readonly DependencyProperty MaxTranslateYProperty =
            DependencyProperty.Register("MaxTranslateY", typeof(double), typeof(PanView), new PropertyMetadata(null));
    }
}
